#include "snake.h"

//主函数
int main(void){
    srand(time(NULL));//随机数种子
    SetConsoleOutputCP(65001);// 设置控制台代码页为UTF-8
    //设置光标不可见
    CONSOLE_CURSOR_INFO cci;
    cci.dwSize = sizeof(cci);
    cci.bVisible = false;
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci);

    readMaxScore();

    while (1)
    {
        displayMenu();
        menuChoiceStart:
        choice = getch();

        switch (choice)
        {
        case '1':
            startGame();
            endGame();
            break;
        case '2':
            displayMaxScore();
            break;
        case '3':
            displayInstruction();
            break;
        case 'q':
        case 'Q':
            exit(0);
        default:
            setColor(RED);
            printf("请输入正确的选项！\n");
            resetColor();
            goto menuChoiceStart;
        }
    }
    
    return 0;
}

void displayInstruction(void){
    system("cls");
    setColor(LIGHTBLUE);
    printf("========== 游戏说明 ==========\n");
    printf("按W、A、S、D分别对应向上、向左、向下、向右移动\n");
    printf("尽可能多地吃到地图中的食物以获得更高分数，默认每个食物提供10分\n");
    printf("按下Q键加速将有得分加成，每加速一次食物得分+1\n");
    printf("按下E键减速将有得分负加成，每减速一次食物得分-1\n");
    printf("吃到食物后蛇将变长并加速\n");
    printf("如果蛇撞到地图边界（蓝色边框和屏幕边框围成的矩形方框）或自身则游戏结束\n");
    resetColor();
    printf("\n");
    printf("按任意键回到主菜单\n");

    getch();

    return;
}

void displayMenu(void){
    system("cls");

    printf("========== 贪吃蛇 ==========\n");
    printf("1. 开始游戏\n");
    printf("2. 最高分\n");
    printf("3. 游戏说明\n");
    printf("q. 退出\n");
    printf("请选择: \n");

    return;
}

void displayMaxScore(void){
    system("cls");
    setColor(YELLOW);
    printf("最高分是：%d\n",maxScore);
    resetColor();
    printf("\n");
    printf("按1回到主菜单，按2重置最高分\n");

    displayMaxScoreChoice:
    choice = getch();
    switch (choice)
    {
    case '1':
        break;
    case '2':
        FILE *file = fopen("save.txt", "w");
        fprintf(file, "0");  // 写入初始值0
        fclose(file);
        maxScore = 0;
        break;
    default:
        setColor(RED);
        printf("请输入正确的选项！\n");
        resetColor();
        goto displayMaxScoreChoice;
    }

    return;
}

void initSnake(void){
    snake.size = 2;

    snake.body[0].x = (int)(WIDTH / 2);
    snake.body[0].y = (int)(HEIGHT / 2);
    snake.body[1].x = (int)(WIDTH / 2) - 1;
    snake.body[1].y = (int)(HEIGHT / 2);

    return;
}

bool ifOverlap(void){
    for (size_t i = 0; i < snake.size; i++)
    {
        if (food.x == snake.body[i].x && food.y == snake.body[i].y)
        {
            return true;
        }
        
    }
    
    return false;
}

void initFood(void){
    do
    {
        food.x = rand() % WIDTH;
        food.y = rand() % HEIGHT;
    } while (ifOverlap());
    
    return;
}

void drawFood(void){
    coord.X = food.x;
    coord.Y = food.y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    setColor(YELLOW);
    printf(FOOD);
    resetColor();

    return;
}

void drawSnake(void){
    for (size_t i = 0; i < snake.size; i++) 
    {
        coord.X = snake.body[i].x;
        coord.Y = snake.body[i].y;
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
        
        setColor(GREEN);
        if (i==0) printf(SNAKE_HEAD);
        else printf(SNAKE_BODY);
        resetColor();
    }

    return;
}

void getDirection(void){
    char temp1;//防止乱按，加一个状态回溯

    if (kbhit()){
        directionNew = tolower(getch());
    }
        
    if(!ifDirectionCollide() && ifDirectionNewCorrect()){
        temp1 = direction;
        direction = directionNew;
        switch (direction){
        case 'w': 
            kx = 0; ky = -1; 
            break;
        case 'a': 
            kx = -1; ky = 0; 
            break;
        case 's': 
            kx = 0; ky = 1; 
            break;
        case 'd': 
            kx = 1; ky = 0; 
            break;
        case 27:
            gameStatus = 0;
            break;
        case 'q':
            speedUp();
            direction = temp1;
            directionNew = temp1;//还原，防止连续加分
            break;
        case 'e':
            speedDown();
            direction = temp1;
            directionNew = temp1;//还原，防止连续减分
            break;
        default : 
            direction = temp1;
            //还原，防止因乱按导致能调头
            break;
        }
    }

    return;
}

bool inField(void){
    if (snake.body[0].x >= 0 && snake.body[0].x < WIDTH
    && snake.body[0].y >= 0 && snake.body[0].y < HEIGHT)
    {
        return true;
    }
    
    return false;
}

bool ifCrash(void){
    for (size_t i = 1; i < snake.size; i++)
    {
        if (snake.body[0].x == snake.body[i].x
        && snake.body[0].y == snake.body[i].y)
        {
            return true;
        }
    }

    return false;
}

bool ifEat(void){
    if(snake.body[0].x == food.x && snake.body[0].y == food.y){
        return true;
    }

    return false;
}

void drawMap(void){
    for (size_t i = 0; i <= HEIGHT; i++)
    {
        for (size_t j = 0; j <= WIDTH; j++)
        {
            if(i == HEIGHT){
                setColor(BACKGROUND_BLUE | BACKGROUND_GREEN);
                printf(" ");
                resetColor();
            } 
            else if(j == WIDTH){
                setColor(BACKGROUND_BLUE | BACKGROUND_GREEN);
                printf(" ");
                resetColor();
            }
            else{
                setColor(CYAN);
                printf(BACKGROUND);
                resetColor();
            }
        }
        
        printf("\n");
    }
    
    return;
}

void updateInformation(void){
    setColor(YELLOW);
    coord.X = WIDTH + 3;
    coord.Y = 0;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    printf("目前得分：%d\n",score);
    coord.X = WIDTH + 3;
    coord.Y = 2;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    printf("每吃一个食物加%d分\n",foodScore);
    resetColor();

    return;
}

void displayInformation(void){
    setColor(YELLOW);
    coord.X = WIDTH + 3;
    coord.Y = 0;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    printf("目前得分：0\n");
    coord.X = WIDTH + 3;
    coord.Y = 1;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    printf("最高分：%d\n",maxScore);
    coord.X = WIDTH + 3;
    coord.Y = 2;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    printf("每吃一个食物加%d分\n",foodScore);
    resetColor();

    return;
}

void snakeMove(void){
    for (size_t i = snake.size-1; i > 0; i--)
    {
        snake.body[i].x = snake.body[i-1].x;
        snake.body[i].y = snake.body[i-1].y;
    }
    snake.body[0].x += kx;
    snake.body[0].y += ky;
    
    return;
}

void startGame(void){
    //初始化数据
    gameStatus = 1;
    direction = 'd';
    directionNew = 'd';
    score = 0;
    sleepTime = 150;
    foodScore = 10;

    //初始化界面，然后除了蛇和游戏信息以外的对象就不更新了。
    system("cls");
    drawMap();
    initSnake();
    initFood();
    drawSnake();
    drawFood();
    displayInformation();
    
    while (inField())
    {   
        //获取输入
        getDirection();
        
        if (gameStatus == 0) return;
        
        //移动前获取尾巴坐标，用于身体变长的功能
        snakeTail.x = snake.body[snake.size-1].x;
        snakeTail.y = snake.body[snake.size-1].y;

        //蛇移动
        snakeMove();

        if(ifCrash()) return;

        if (ifEat())
        {
            ifEatFood = true;
            //更新食物
            initFood();
            drawFood();
            //身体增长
            snake.size++;
            snake.body[snake.size-1].x = snakeTail.x;
            snake.body[snake.size-1].y = snakeTail.y;
            //加分
            scorePlus();
            //加速
            speedUp();
        }

        //更新蛇
        updateSnake();
        //更新游戏信息
        updateInformation();
        //重置游戏数值
        ifEatFood = false;

        Sleep(sleepTime);//刷新率
    }

    return;
}

void speedUp(void){
    if (sleepTime >= MIN_REFRESH_RATE)
    {
        sleepTime -= 10;
        foodScore += 1;
    }

    return;
}

void speedDown(void){
    if (sleepTime <= MAX_REFRESH_RATE)
    {
        sleepTime += 10;
        foodScore -= 1;
    }

    return;
}

bool ifDirectionCollide(void){
    if ((direction == 'w' && directionNew == 's')
    || (direction == 's' && directionNew == 'w')
    || (direction == 'a' && directionNew == 'd')
    || (direction == 'd' && directionNew == 'a'))
    {
        return true;
    }
    
    return false;
}

bool ifDirectionNewCorrect(void){
    if(directionNew == 'w'
    || directionNew == 'a'
    ||directionNew == 's'
    ||directionNew == 'd'
    ||directionNew == 27
    ||directionNew == 'q'
    ||directionNew == 'e'){
        return true;
    }

    return false;
}

void updateSnake(void){
    if(ifEatFood){
        coord.X = snake.body[0].x;
        coord.Y = snake.body[0].y;
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
        setColor(GREEN);
        printf(SNAKE_HEAD);
        resetColor();
    }else{
        coord.X = snake.body[0].x;
        coord.Y = snake.body[0].y;
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
        setColor(GREEN);
        printf(SNAKE_HEAD);
        resetColor();
        coord.X = snakeTail.x;
        coord.Y = snakeTail.y;
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
        setColor(CYAN);
        printf(BACKGROUND);
        resetColor();
    }
    coord.X = snake.body[1].x;
    coord.Y = snake.body[1].y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    setColor(GREEN);
    printf(SNAKE_BODY);
    resetColor();

    return;
}

void endGame(void){
    system("cls");
    setColor(RED);
    printf("Game Over!\n");
    resetColor();
    setColor(YELLOW);
    printf("你的得分是：%d，",score);
    if (ifBreak()) printf("恭喜你打破了记录！\n");
    else printf("最高分是%d，再接再厉！\n",maxScore);
    resetColor();
    printf("\n");
    printf("按1回到主菜单，按2退出\n");

    updateMaxScore();

    endGameChoiceStart:
    choice = getch();
    switch (choice)
    {
    case '1':
        break;
    case '2':
        exit(0);
    default:
        setColor(RED);
        printf("请输入正确的选项！\n");
        resetColor();
        goto endGameChoiceStart;
    }

    return;
}

void updateMaxScore(void){
    maxScore = score > maxScore ? score : maxScore;
    
    FILE *file = fopen("save.txt", "w");
    if (file != NULL) { 
        fprintf(file, "%d", maxScore);
        fclose(file);
    }
}

bool ifBreak(void){
    if (score > maxScore) return true;

    return false;
}

void readMaxScore(void){
    FILE *file = fopen("save.txt","r");

    if (file != NULL) {
        // 文件存在，读取最高分
        if (fscanf(file, "%d", &maxScore) != 1) {
            maxScore = 0;  // 文件内容无效时设为0
        }
        fclose(file);
    } else {
        // 文件不存在，创建并初始化为0
        file = fopen("save.txt", "w");
        fprintf(file, "0");  // 写入初始值0
        fclose(file);
        maxScore = 0;
    }
    
    return;
}

void scorePlus(void){
    score += foodScore;

    return;
}

void setColor(int color){
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
    
    return;
}

void resetColor(void){
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), RED | GREEN | BLUE);

    return;
}
